home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / dos / pascal / ovract.com / OVRACTR.PAS < prev   
Encoding:
Pascal/Delphi Source File  |  1989-11-22  |  17.0 KB  |  638 lines

  1. {
  2. OVRACTR generates reports describing the overlay activity of a Turbo Pascal
  3. 5.x program.
  4.  
  5. To use it, add the unit OVRACT as near as possible to the beginning of the uses
  6. statement of your main program.  Compile the application to create both an
  7. EXE file and a corresponding MAP file and run the program normally.  A file
  8. named ProgName.OVD will be produced, where ProgName is the root name of your
  9. EXE file.  OVRACTR must read the first section of the MAP file (the segment
  10. map) to get certain information. It also reads from the OVD file to get
  11. detailed information about the overlay activity. It doesn't need to access
  12. the EXE or OVR files.
  13.  
  14. Call OVRACTR as follows:
  15.  
  16.   OVRACTR [Options] ProgName [>Output]
  17.  
  18. OVRACTR forces the extension 'MAP' onto ProgName to find the MAP file, and
  19. the extension 'OVD' onto ProgName to find the overlay data file. The overlay
  20. report is written to the standard output and may be redirected to a file or
  21. to the printer. The options are
  22.  
  23.    /Q   stops OVRACTR from writing status messages while it works.
  24.    /D   produces the detailed report of all overlay activity.
  25.    /S   produces the summary report showing statistics for each unit.
  26.    /O   produces the summary report showing statistics only for overlaid units
  27.  
  28. The detailed report gives a very basic dump of the events in the OVD file.
  29. For each event it shows the time, name of the unit, offset of the procedure,
  30. and type of event, followed by a list of the names of the units on the load
  31. list.
  32.  
  33. The summary report is quite a bit more civilized, being a clone of
  34. Kim Kokkonen's OVRSIZ report, with columns added for load count and
  35. reprieve count.
  36.  
  37. Written by Ron Schuster (CIS 76666,2322).  Copyright (c) 1989.
  38. All rights reserved.  May be distributed freely, but not for a profit.
  39.  
  40. This program was originally based on the overlay profiler OVRPROF
  41. written by Richard Casey (CIS 72247,151).
  42.  
  43. Portions of this program originally appeared in OVRSIZ by Kim Kokkonen,
  44. TurboPower Software (CIS 76004,2611), and were used with the permission of
  45. the author.  Copyright (c) 1989, TurboPower Software. All rights reserved.
  46. May be distributed freely, but not for a profit.
  47.  
  48.  
  49. Version 1.0, 11/21/89
  50. --------------------
  51.   Initial release.
  52. }
  53.  
  54. {$R-,S-,I-,V-,F-,B-}
  55.  
  56. program OVRACTR;
  57.   {-Generate reports of overlay activity from data produced by OVRACT}
  58. uses Dos;
  59. const
  60.   Version = '1.0';                {Version number}
  61.   MaxUnits = 255;                 {Maximum number of units to report}
  62.   NameSize = 15;                  {Maximum reported segment name length}
  63.   BufSize = 1024;                 {Size of text I/O buffer}
  64.   ShowStatus : Boolean = True;    {True to keep status running during operation}
  65.   ShowDetail : Boolean = False;   {True to write detailed dump of overlay events}
  66.   ShowSummary : Boolean = False;  {True to write summary report}
  67.   OverlaysOnly : Boolean = False;  {True to write summary report with overlaid
  68.                                     units only}
  69.   Digits : array[0..$F] of Char = '0123456789ABCDEF';
  70.   DosDelimSet : set of Char = ['\', ':', #0];
  71.  
  72. type
  73.   UnitNameStr = string[NameSize];
  74.   UnitRecord = record
  75.     Name : UnitNameStr;
  76.     SegClass : Word;
  77.     StatSeg : Word;
  78.     StatLen : LongInt;
  79.     FileOfs : LongInt;
  80.     CodeSize : Word;
  81.     FixupSize : Word;
  82.     EntryPts : Word;
  83.     LoadCount : Word;
  84.     ReprieveCount : Word;
  85.   end;
  86.  
  87.   Long =
  88.     record
  89.       LowWord, HighWord : Word;
  90.     end;
  91.  
  92. var
  93.   UnitCount    : Word;
  94.   Units        : array[0..MaxUnits] of UnitRecord;
  95.   StdErr       : text;
  96.   OvrDataFile  : file of Word;
  97.   PSP          : Word;
  98.   Ovr_Heap_Org : Word;
  99.   Ovr_Heap_End : Word;
  100.   Buffer       : array[1..BufSize] of Char; {Text buffer for map file}
  101.   Dname        : PathStr;               {Input OVD file name}
  102.   Mname        : PathStr;               {Input MAP file name}
  103.   Time         : LongInt;
  104.  
  105. procedure WriteCopyRight;
  106.   {-Copyright notice in object code and on screen}
  107. begin
  108.   WriteLn(StdErr, 'TP5.x Overlay Activity Reporter, by Ron Schuster. Version ', Version);
  109. end;
  110.  
  111. procedure OpenStdErr;
  112.   {-Open standard error device}
  113. begin
  114.   Assign(StdErr, '');
  115.   Rewrite(StdErr);
  116.   with TextRec(StdErr) do begin
  117.     Handle := 2;
  118.     BufSize := 1;
  119.   end;
  120. end;
  121.  
  122. procedure Error(Msg : String);
  123.   {-Report error and halt}
  124. begin
  125.   if Msg <> '' then
  126.     WriteLn(StdErr, Msg);
  127.   Halt(1);
  128. end;
  129.  
  130. procedure InvalidMapError;
  131.   {-Common error}
  132. begin
  133.   Error('Invalid MAP file format');
  134. end;
  135.  
  136. function HexW(W : Word) : String;
  137.   {-Return hex string for word}
  138. begin
  139.   HexW[0] := #4;
  140.   HexW[1] := Digits[Hi(W) shr 4];
  141.   HexW[2] := Digits[Hi(W) and $F];
  142.   HexW[3] := Digits[Lo(W) shr 4];
  143.   HexW[4] := Digits[Lo(W) and $F];
  144. end;
  145.  
  146. function HexL(L : LongInt) : String;
  147.   {-Return hex string for LongInt}
  148. begin
  149.   with Long(L) do
  150.     HexL := HexW(HighWord)+HexW(LowWord);
  151. end;
  152.  
  153. function Long2Str(L : LongInt) : String;
  154.   {-Convert a long/word/integer/byte/shortint to a string}
  155. var
  156.   S : String;
  157. begin
  158.   Str(L, S);
  159.   Long2Str := S;
  160. end;
  161.  
  162. function StUpcase(S : String) : String;
  163.   {-Return the uppercase of a string}
  164. var
  165.   I : Integer;
  166. begin
  167.   for I := 1 to Length(S) do
  168.     S[I] := Upcase(S[I]);
  169.   StUpcase := S;
  170. end;
  171.  
  172. function Pad(S : String; Len : Byte) : String;
  173.   {-Return a string right-padded to length len with ch}
  174. var
  175.   O : String;
  176. begin
  177.   if Length(S) >= Len then
  178.     Pad := S
  179.   else begin
  180.     O[0] := Chr(Len);
  181.     Move(S[1], O[1], Length(S));
  182.     FillChar(O[Succ(Length(S))], Len-Length(S), ' ');
  183.     Pad := O;
  184.   end;
  185. end;
  186.  
  187. function TrimLead(S : String) : String;
  188.   {-Return a string with leading white space removed}
  189. begin
  190.   while (Length(S) > 0) and (S[1] <= ' ') do
  191.     Delete(S, 1, 1);
  192.   TrimLead := S;
  193. end;
  194.  
  195. function Trim(S : String) : String;
  196.   {-Return a string with leading and trailing white space removed}
  197. begin
  198.   while (Length(S) > 0) and (S[Length(S)] <= ' ') do
  199.     Dec(S[0]);
  200.   while (Length(S) > 0) and (S[1] <= ' ') do
  201.     Delete(S, 1, 1);
  202.   Trim := S;
  203. end;
  204.  
  205. function GetLong(var S : String; var L : LongInt) : Boolean;
  206.   {-Parse next longint out of line S}
  207. var
  208.   Num : String[8];
  209.   Code : Word;
  210. begin
  211.   S := TrimLead(S);
  212.   Num := '';
  213.   while (Length(S) > 0) and (Pos(S[1], Digits) <> 0) do begin
  214.     Num := Num+S[1];
  215.     Delete(S, 1, 1);
  216.   end;
  217.   if Length(Num) = 0 then begin
  218.     GetLong := False;
  219.     Exit;
  220.   end;
  221.   if (Length(S) > 0) and (Upcase(S[1]) = 'H') then begin
  222.     Num := '$'+Num;
  223.     Delete(S, 1, 1);
  224.   end;
  225.   Val(Num, L, Code);
  226.   GetLong := (Code = 0);
  227. end;
  228.  
  229. function GetName(var S, Name : String) : Boolean;
  230.   {-Parse next alphanumeric name from string s}
  231. begin
  232.   S := TrimLead(S);
  233.   Name := '';
  234.   while (Length(S) > 0) and (S[1] > ' ') do begin
  235.     if Length(Name) < NameSize then
  236.       Name := Name+S[1];
  237.     Delete(S, 1, 1);
  238.   end;
  239.   GetName := (Name <> '');
  240. end;
  241.  
  242. function HasExtension(Name : String; var DotPos : Word) : Boolean;
  243.   {-Return whether and position of extension separator dot in a pathname}
  244. var
  245.   I : Word;
  246. begin
  247.   DotPos := 0;
  248.   for I := Length(Name) downto 1 do
  249.     if (Name[I] = '.') and (DotPos = 0) then
  250.       DotPos := I;
  251.   HasExtension := (DotPos > 0) and (Pos('\', Copy(Name, Succ(DotPos), 64)) = 0);
  252. end;
  253.  
  254. function ForceExtension(Name, Ext : String) : String;
  255.   {-Return a pathname with the specified extension attached}
  256. var
  257.   DotPos : Word;
  258. begin
  259.   if HasExtension(Name, DotPos) then
  260.     ForceExtension := Copy(Name, 1, DotPos)+Ext
  261.   else
  262.     ForceExtension := Name+'.'+Ext;
  263. end;
  264.  
  265. procedure WriteHelp;
  266.   {-Display help information and halt}
  267. begin
  268.   WriteLn;
  269.   WriteLn('Usage: OVRACTR [Options] InputName [>OutputFile]');
  270.   WriteLn;
  271.   WriteLn('  OVRACTR must read:');
  272.   WriteLn('    InputName.MAP - symbol file for segment information.');
  273.   WriteLn('    InputName.OVD - overlay data file produced by OVRACT.');
  274.   WriteLn;
  275.   WriteLn('Options:');
  276.   WriteLn('  /Q    Quiet mode. No status output while processing.');
  277.   WriteLn('  /D    Detail report showing all overlay activity.');
  278.   WriteLn('  /S    Summary report showing statistics for each unit.');
  279.   WriteLn('  /O    Summary report showing statistics only for overlaid units.');
  280.   WriteLn;
  281.   WriteLn('  At least one report option must be specified');
  282.   Halt(1);
  283. end;
  284.  
  285. function ExistFile(FName : String) : Boolean;
  286.   {-Return true if file exists}
  287. var
  288.   F : file;
  289. begin
  290.   Assign(F, FName);
  291.   Reset(F);
  292.   if IoResult = 0 then begin
  293.     ExistFile := True;
  294.     Close(F);
  295.   end else
  296.     ExistFile := False;
  297. end;
  298.  
  299. procedure ValidateInput;
  300.   {-Get working filenames and assure files exist}
  301. var
  302.   Iroot : PathStr;
  303.   Arg : String;
  304.   I : Integer;
  305. begin
  306.   {Get parameters}
  307.   Iroot := '';
  308.   I := 1;
  309.   while I <= ParamCount do begin
  310.     Arg := StUpcase(ParamStr(I));
  311.     if (Arg = '/Q') or (Arg = '-Q') then
  312.       ShowStatus := False
  313.     else if (Arg = '/D') or (Arg = '-D') then
  314.       ShowDetail := True
  315.     else if (Arg = '/S') or (Arg = '-S') then
  316.       ShowSummary := True
  317.     else if (Arg = '/O') or (Arg = '-O') then begin
  318.       ShowSummary := True;
  319.       OverlaysOnly := True;
  320.     end
  321.     else if Iroot = '' then
  322.       Iroot := Arg
  323.     else
  324.       Error('Too many filenames on command line');
  325.     Inc(I);
  326.   end;
  327.   if (Iroot = '') or not (ShowDetail or ShowSummary) then
  328.     WriteHelp;
  329.  
  330.   {Build working filenames}
  331.   Dname := ForceExtension(Iroot, 'OVD');
  332.   Mname := ForceExtension(Iroot, 'MAP');
  333.  
  334.   {Make sure files are OK}
  335.   if not ExistFile(Dname) then
  336.     Error('OVD file '+Dname+' not found');
  337.   if not ExistFile(Mname) then
  338.     Error('MAP file '+Mname+' not found');
  339. end;
  340.  
  341. procedure ParseMapFile(FName : String);
  342.   {-Read and parse the MAP file, guaranteed to exist}
  343. var
  344.   F : Text;
  345.   S : String;
  346.   StatStart : LongInt;
  347.   SegType : String;
  348.   Tlong : LongInt;
  349.   ParseState : (Unknown, Segments, Done);
  350. begin
  351.  
  352.   {Open up the MAP file for reading}
  353.   Assign(F, FName);
  354.   SetTextBuf(F, Buffer, BufSize);
  355.   Reset(F);
  356.   if IoResult <> 0 then
  357.     Error('Error opening '+FName);
  358.  
  359.   if ShowStatus then
  360.     WriteLn(StdErr, 'Parsing MAP file');
  361.  
  362.   {Parse the segment description section only}
  363.   UnitCount := 0;
  364.   ParseState := Unknown;
  365.   repeat
  366.     ReadLn(F, S);
  367.     if IoResult <> 0 then
  368.       Error('Error reading '+FName);
  369.     S := StUpcase(Trim(S));
  370.     if S <> '' then
  371.       if Pos('START', S) = 1 then
  372.         ParseState := Segments
  373.       else if Pos('ADDRESS', S) = 1 then
  374.         ParseState := Done
  375.       else if ParseState = Segments then begin
  376.         {Parse the line to get the unit description}
  377.         Inc(UnitCount);
  378.         if UnitCount > MaxUnits then
  379.           Error('Cannot exceed '+Long2Str(MaxUnits)+' segments');
  380.         FillChar(Units[UnitCount], SizeOf(UnitRecord), 0);
  381.  
  382.         with Units[UnitCount] do begin
  383.  
  384.           {Get the position and size of the unit in the EXE image}
  385.           if not GetLong(S, StatStart) then
  386.             InvalidMapError;
  387.           StatSeg := StatStart shr 4;
  388.           {Ignore the end of the segment}
  389.           if not GetLong(S, Tlong) then
  390.             InvalidMapError;
  391.           {Get the length of the segment}
  392.           if not GetLong(S, StatLen) then
  393.             InvalidMapError;
  394.  
  395.           {Get the name of the segment}
  396.           if not GetName(S, Name) then
  397.             InvalidMapError;
  398.  
  399.           {Some segments are not really in the EXE file}
  400.           if not GetName(S, SegType) then
  401.             InvalidMapError;
  402.           if SegType = 'CODE' then
  403.             SegClass := 0
  404.           else if SegType = 'DATA' then
  405.             SegClass := 1
  406.           else if SegType = 'STACK' then
  407.             SegClass := 2
  408.           else if SegType = 'HEAP' then
  409.             SegClass := 3
  410.           else
  411.             SegClass := 4;
  412.         end;
  413.       end;
  414.   until (ParseState = Done) or EoF(F);
  415.   Close(F);
  416. end;
  417.  
  418. function ReadWord : Word;
  419. var
  420.   W : Word;
  421. begin
  422.   if EOF (OvrDataFile) then
  423.     Error ('Unexpected EOF on ' + Dname);
  424.   Read (OvrDataFile, W);
  425.   ReadWord := W;
  426. end;
  427.  
  428. function LookupSeg (S : Word) : Integer;
  429. var
  430.   I : Integer;
  431. begin
  432.   for I := 1 to UnitCount do
  433.     if (S = Units[I].StatSeg) and (Units[I].StatLen > 0) then begin
  434.       LookUpSeg := I;
  435.       exit;
  436.     end;
  437.   LookUpSeg := 0;
  438. end;
  439.  
  440. function NextPara(Bytes : LongInt) : LongInt;
  441.   {-Round up to next paragraph}
  442. begin
  443.   NextPara := (Bytes+15) and $FFFFFFF0;
  444. end;
  445.  
  446. procedure ProcessCodeList;
  447. var
  448.   StaticSeg : Word;
  449. begin
  450.   StaticSeg := ReadWord;
  451.   while StaticSeg <> 0 do begin
  452.     with Units[LookupSeg(StaticSeg)] do begin
  453.       with Long(FileOfs) do begin
  454.         LowWord := ReadWord;
  455.         HighWord := ReadWord;
  456.       end;
  457.       CodeSize := NextPara(ReadWord);
  458.       FixupSize := NextPara(ReadWord);
  459.       EntryPts := ReadWord;
  460.     end;
  461.     StaticSeg := ReadWord;
  462.   end;
  463. end;
  464.  
  465. function FormatTime (T : LongInt) : string;
  466. const
  467.   Divisor = 119318.0/65536;
  468. var
  469.   Tenths : LongInt;
  470.   Minutes, I : Integer;
  471.   S : string[8];
  472.   Secs : string[3];
  473. begin
  474.   Tenths := Round (T / Divisor);
  475.   Minutes := Tenths div 600;
  476.   Tenths := Tenths mod 600;
  477.   Str (Minutes:3, S);
  478.   Str (Tenths:3, Secs);
  479.   S := S + ':' + Secs;
  480.   for I := 1 to 6 do
  481.     if S[I] = ' ' then
  482.       S[I] := '0';
  483.   Insert ('.',S,7);
  484.   FormatTime := S;
  485. end;
  486.  
  487. procedure ChangeOverlayBuffer;
  488. begin
  489.   Ovr_Heap_Org := ReadWord;
  490.   Ovr_Heap_End := ReadWord;
  491.   if ShowDetail then
  492.     Writeln ('Overlay buffer set to ',HexW(Ovr_Heap_Org),'-',HexW(Ovr_Heap_End));
  493. end;
  494.  
  495. procedure PrintLoadList (W : Word);
  496. var
  497.   I : Integer;
  498.   OutSize : Integer;   { Count of characters written on current line }
  499.   Name : UnitNameStr;
  500.   StaticSeg,
  501.   LoadSeg : Word;
  502.   OvrSeg : Word;  { Static segment of unit just loaded }
  503.   OvrOfs : Word;  { Offset of procedure within the unit's static segment }
  504. begin
  505.   OvrSeg := W;
  506.   OvrOfs := ReadWord;
  507.   StaticSeg := ReadWord;
  508.   I := LookupSeg (OvrSeg - PSP - $10);
  509.   if ShowDetail then
  510.     Write (FormatTime (Time),': ', Units[I].Name, '.', HexW(OvrOfs), ' called');
  511.   if StaticSeg = 0 then begin
  512.     { there is no load list, must be a reprieve }
  513.     if ShowDetail then
  514.       Writeln (' (reprieved)');
  515.     Inc(Units[I].ReprieveCount);
  516.   end
  517.   else begin
  518.     { load list follows }
  519.     Inc(Units[I].LoadCount);
  520.     if ShowDetail then begin
  521.       Writeln (' (loaded)');
  522.       Write ('  load list: ');
  523.       OutSize := 13;
  524.     end;
  525.     while StaticSeg <> 0 do begin
  526.       LoadSeg := ReadWord;
  527.       if ShowDetail then begin
  528.         Name := Units[LookupSeg(StaticSeg-PSP-$10)].Name;
  529.         if OutSize + Length(Name) + 2 > 79 then begin
  530.           Writeln;
  531.           Write ('  ');
  532.           OutSize := 2;
  533.         end;
  534.         Write (Name,'  ');
  535.         Inc(OutSize,Length(Name) + 2);
  536.       end;
  537.       StaticSeg := ReadWord;
  538.     end;
  539.     if ShowDetail then
  540.       Writeln;
  541.   end;
  542. end;
  543.  
  544. procedure ProcessOverlayData (Name : PathStr);
  545. const
  546.   EndListMark : Word = 0;
  547.   OvrHeapMark : Word = $FFFF;
  548. var
  549.   W : Word;
  550. begin
  551.   assign(OvrDataFile,Name);
  552.   {$I-}
  553.   reset(OvrDataFile);
  554.   {$I+}
  555.   if IOResult <> 0 then
  556.     Error ('Could not open '+Name+' for input')
  557.   else begin
  558.     if ShowStatus then
  559.       WriteLn(StdErr, 'Reading OVD file');
  560.     if ReadWord <> 1 then
  561.       Error ('OVD file version mismatch');
  562.     PSP := ReadWord;
  563.     ProcessCodeList;
  564.     while not EOF (OvrDataFile) do begin
  565.       with Long(Time) do begin
  566.         LowWord := ReadWord;
  567.         HighWord := ReadWord;
  568.       end;
  569.       W := ReadWord;
  570.       if W = OvrHeapMark then begin
  571.         ChangeOverlayBuffer;
  572.         W := ReadWord;
  573.       end;
  574.       PrintLoadList (W);
  575.     end;
  576.   end;
  577. end;
  578.  
  579. procedure WriteUnitInfo;
  580. var
  581.   V : Word;
  582. begin
  583.   WriteLn;
  584.   WriteLn(
  585. 'UNIT STATISTICS');
  586.   WriteLn(
  587. '                Static  Static  Overlay  Fixup   Entry  Overlay  Load  Reprieve');
  588.   WriteLn(
  589. 'Segment name   Segment    Size    Size    Size  Points  FilePos  Count  Count');
  590.   WriteLn(
  591. '==============  ======   =====   =====   =====   =====  =======  =====  =====');
  592. {xxxxxxxxxxxxxxx 0FFFFh   ddddd   ddddd   ddddd   ddddd  0FFFFFh  ddddd  ddddd}
  593.  
  594.   for V := 1 to UnitCount do
  595.     with Units[V] do
  596.       if (StatLen > 0) and (not OverlaysOnly or (CodeSize > 0)) then begin
  597.         Write(Pad(Name, NameSize+1),
  598.               '0', HexW(StatSeg), 'h   ',
  599.               StatLen:5, '   ');
  600.  
  601.         if CodeSize > 0 then begin
  602.           { Overlaid Unit }
  603.           Write(CodeSize:5, '   ',
  604.                 FixupSize:5, '   ',
  605.                 EntryPts:5, '  ',
  606.                 Copy(HexL(FileOfs), 3, 6), 'h  ',
  607.                 LoadCount:5, '  ',
  608.                 ReprieveCount:5);
  609.  
  610.         end else begin
  611.           {Non-overlaid unit or other segment}
  612.           Write('    -       -       -        -      -      -');
  613.         end;
  614.         WriteLn;
  615.       end;
  616. end;
  617.  
  618. begin
  619.   {Open standard error device}
  620.   OpenStdErr;
  621.  
  622.   {Display copyright}
  623.   WriteCopyRight;
  624.  
  625.   {Get filenames and assure they exist}
  626.   ValidateInput;
  627.  
  628.   {Parse MAP file to get segment names and locations}
  629.   ParseMapFile(Mname);
  630.  
  631.   {Read overlay data file}
  632.   ProcessOverlayData(Dname);
  633.  
  634.   {Write information}
  635.   if ShowSummary then
  636.     WriteUnitInfo;
  637. end.
  638.